package game;

import java.awt.Point;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;

/**
 * Sets up all the commonalities between each of the pieces for all of the games.
 * 
 * @author Nicole Waldrum
 * @date January 30, 2010
 * @version 1.0
 *
 * @author Mark Kelly
 * @date February 20, 2010
 * @version 2.0
 */
public abstract class Piece implements MoveChecker {
	// Instance Variables
	private String name;
	private Point position;
	private Team team;
	private boolean moved;
	
	/**
	 * Creates a new instance of <tt>Piece</tt>.
	 * 
	 * @param name the name of this <tt>Piece</tt>
	 * @param position the location of this <tt>Piece</tt> on the board
	 * @param team the team that this <tt>Piece</tt> is a part member of
	 */
	public Piece(String name, Point position, Team team) {
		this.name = name;
		this.position = position;
		this.team = team;
		this.moved = false;
	}

	/**
	 * Return a <tt>List\<Point\></tt> that must be free in order for this <tt>Piece</tt> to move to a new location
	 * 
	 * @param dest a <tt>Point</tt> representing the new location
	 * 
	 * @return a List<Point> with every <tt>Point</tt> that must be free
	 */
	public abstract List<Point> checkMovePattern(Point dest);
	
	/**
	 * Sets the location of the <tt>Piece</tt> on the board
	 * 
	 * @param position the new location for this <tt>Piece</tt>
	 */
	public void setLocation (Point position) {
		this.position = position;
		moved = true;
	}

	/**
	 * Returns the current location of the piece on the board
	 * 
	 * @return the location of the <tt>Piece</tt> on the <tt>ChessBoard</tt>.
	 */
	public Point getLocation() {
		return position;	
	}
	
	/**
	 * Returns the team that the piece on
	 * 
	 * @return the team that the piece is on
	 */
	public Team getTeam() {
		return this.team;
	}
	
	/**
	 * Returns a reference to this Piece as the specified type
	 * 
	 * @param <T> the new type
	 * @param clazz a Class<T> representing the new type of the Piece
	 * 
	 * @return a reference of Type T to this Piece
	 * 
	 * @throws InvocationTargetException if the underlying constructor throws an exception. 
	 * @throws IllegalAccessException if this Constructor object enforces Java language access control and the underlying constructor is inaccessible. 
	 * @throws InstantiationException if the class that declares the underlying constructor represents an abstract class. 
	 * @throws IllegalArgumentException if the number of actual and formal parameters differ; if an unwrapping conversion for primitive arguments fails; or if, after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a method invocation conversion; if this constructor pertains to an enum type. 
	 * @throws NoSuchMethodException if a matching method is not found
	 * @throws SecurityException
	 * 
	 * TODO catch some exceptions
	 */
	public <T extends Piece> T changeType(Class<T> clazz) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		// retrieves the constructor for a class of type T
		Constructor<T> c = clazz.getConstructor(String.class, Point.class, Team.class);
		
		// creates a new T from information in this Piece
		return c.newInstance(this.name, this.position, this.team); // creates a new T from information in this Piece
	}
	
	/**
	 * Returns the type of this piece as a String
	 * 
	 * @return the name of the class of the Piece
	 */
	public String getType() {
		return this.getClass().getSimpleName();
	}
	
	/**
	 * Returns the name of this Piece
	 * 
	 * @return the name of this piece
	 */
	public String getName() {
		return this.name;
	}
	
	/**
	 * Returns whether this Piece has moved
	 * 
	 * @return <tt>true</tt> if this Piece moved, false otherwise
	 */
	public boolean hasMoved() {
		return moved;
	}
	
	public boolean isValidMove(Piece piece, Point point) {
		return !getLocation().equals(point);
	}
}